package com.ibm.cps.sql;

import java.util.List;

import net.sf.jsqlparser.statement.select.FromItem;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.ibm.cps.message.MessageFields;
import com.ibm.cps.sql.select.FieldDescriptor;
import com.ibm.util.JSONUtility;
import com.ibm.util.RandomUtil;
import com.ibm.util.exception.CPSException;

/**
 * Created by telekinesis on 6/7/15.
 */
public class ProcessorAttributeFiller {

	public static ObjectNode createBasicNodeWithSingleParent(
			String processorId, String type, String groupId,
			String parentProcessorId) {
		ObjectMapper mapper = new ObjectMapper();
		ObjectNode node = mapper.createObjectNode();
		node.put(MessageFields.PROCESSORID, processorId);
		node.put(MessageFields.PARENTPROCESSORS, parentProcessorId);
		node.put(MessageFields.GROUPID, groupId);
		node.put(MessageFields.TYPE, type);
		return node;

	}

	public static ObjectNode createBasicNodeWithMultipleParent(
			String processorId, String type, String groupId,
			String... parentProcessorId) {
		ObjectMapper mapper = new ObjectMapper();
		ObjectNode node = mapper.createObjectNode();
		node.put(MessageFields.PROCESSORID, processorId);
		ArrayNode parentObjects = mapper.createArrayNode();
		for (String parent : parentProcessorId) {
			parentObjects.add(parent);
		}
		node.put(MessageFields.GROUPID, groupId);
		node.set(MessageFields.PARENTPROCESSORS, parentObjects);
		node.put(MessageFields.TYPE, type);
		return node;
	}

	public static String createProcessorId() {
		return RandomUtil.getRandomId();
	}

	public static ObjectNode findInputProcessor(FromItem fromItem,
			String groupId) throws CPSException {
		String dataSourceName = removeQuotes(fromItem.toString());
		ObjectNode inputProcessorMeta = JSONUtility.newObjectNode();
		String id = RandomUtil.getRandomId();
		inputProcessorMeta.put(MessageFields.PROCESSORID, id);
		inputProcessorMeta.put(MessageFields.GROUPID, groupId);
		// now exist two input type. May confirm.
		inputProcessorMeta.put(MessageFields.TYPE, MessageFields.INPUT_TYPE);
		inputProcessorMeta.put(MessageFields.NAME, dataSourceName);
		// String inputProcessorMetaString = factory
		// .replicateDataSource(topicName);

		// if (inputProcessorMetaString == null) {
		// throw new CPSException(ErrorCode.DATA_NOT_INSERTED_CODE,
		// "Data source for topic: " + topicName + " not specified.");
		// }
		// ObjectNode node = JSONUtility
		// .fromObjectString(inputProcessorMetaString);
		return inputProcessorMeta;
	}

	public static ObjectNode createProjectionNode(String processorId,
			List<FieldDescriptor> descriptors, String parentProcessorId,
			String ruleName, String groupId) {
		ObjectNode projectNode = ProcessorAttributeFiller
				.createBasicNodeWithMultipleParent(processorId,
						MessageFields.PROJECTION_TYPE, groupId,
						parentProcessorId);
		projectNode.put(MessageFields.NAME, ruleName + "_projection");
		ArrayNode arr = projectNode.putArray("outputFields");
		for (FieldDescriptor d : descriptors) {
			arr.add(d.getTargetColumn());
		}
		return projectNode;
	}

	// {"processorid":"0000000000ceab2e30f3154d","rulename":"user given name ","rule":"phase>300"
	// ,"parentprocessors":["0000000000ed66138a01299f"],"type":"rule","model":false
	// ,"topicname":"output topic name","userid":"123456789012345678901239"}
	public static ObjectNode createRuleNode(String processorId,
			String outputTopicName, String parentProcessorId, boolean isEvent,
			String ruleName, String rule, String groupId) {
		ObjectNode ruleNode = ProcessorAttributeFiller
				.createBasicNodeWithSingleParent(processorId,
						MessageFields.RULE, groupId, parentProcessorId);
		ruleNode.put(MessageFields.RULENAME, ruleName + "_rule");
		ruleNode.put(MessageFields.RULE, rule);
		ruleNode.put(MessageFields.TOPICNAME, removeQuotes(outputTopicName));
		if (isEvent) {
			ruleNode.put(MessageFields.MODEL, 3);
		} else {
			ruleNode.put(MessageFields.MODEL, 0);
		}
		return ruleNode;
	}

	// {"processorid":"00000000014ddeaee0eb2215","windowname":"tight"
	// ,"size":10, "step":1
	// ,"parentprocessors":["0000000000e8151c09017eae"],"type":"window"
	// ,"userid":"123456789012345678901239"}
	public static ObjectNode createWindowNode(String processorId,
			String parentProcessorId, int step, int size, String ruleName,
			String groupId) {
		ObjectNode node = ProcessorAttributeFiller
				.createBasicNodeWithMultipleParent(processorId,
						MessageFields.SLIDING_WINDOW_TYPE, groupId, parentProcessorId);
		String windowName = ruleName + "_windowing";
		node.put(MessageFields.NAME, windowName);
		node.put(MessageFields.STEP, step);
		node.put(MessageFields.SIZE, size);
		return node;
	}

	// {"processorid":"00000000014ddeaee0eb2215","name":"tight"
	// ,"size":10, "step": 1, "coefficient": 2, "attributename": "targetfield"
	// ,"parentprocessors":["0000000000e8151c09017eae"],"type":"DFT"
	// ,"userid":"123456789012345678901239"}

	public static ObjectNode createDftNode(String processorId,
			String parentProcessorId, int coefficientsToOutput,
			String outputFieldName, String ruleName, String groupId) {
		ObjectNode node = ProcessorAttributeFiller
				.createBasicNodeWithMultipleParent(processorId,
						MessageFields.BATCH_DFT_TYPE, groupId,
						parentProcessorId);
		node.put(MessageFields.NAME, ruleName + "_DFT");
		node.put(MessageFields.COEFFICIENT, coefficientsToOutput);
		node.put(MessageFields.OUTPUT_FIELD_NAME, removeQuotes(outputFieldName));
		return node;
	}

	public static ObjectNode createExtremumNode(String processorId,
			String parentProcessorId, String outputFieldName, boolean isMin,
			String ruleName, String groupId) {
		ObjectNode node = ProcessorAttributeFiller
				.createBasicNodeWithMultipleParent(processorId,
						MessageFields.EXTREME_TYPE, groupId, parentProcessorId);
		node.put(MessageFields.NAME, ruleName + "_Extremum");
		node.put(MessageFields.OUTPUT_FIELD_NAME, outputFieldName);
		return node;
	}

	public static ObjectNode createAverageNode(String processorId,
			String parentProcessorId, String outputFieldName, String ruleName,
			String groupId) {
		ObjectNode node = ProcessorAttributeFiller
				.createBasicNodeWithMultipleParent(processorId,
						MessageFields.AVERAGE_TYPE, groupId, parentProcessorId);
		node.put(MessageFields.NAME, ruleName + "_Average");
		node.put(MessageFields.OUTPUT_FIELD_NAME, removeQuotes(outputFieldName));
		return node;
	}

	public static ObjectNode createVarianceNode(String processorId,
			String parentProcessorId, String outputFieldName, String ruleName,
			String groupId) {
		ObjectNode node = ProcessorAttributeFiller
				.createBasicNodeWithMultipleParent(processorId,
						MessageFields.VARIANCE_TYPE, groupId, parentProcessorId);
		node.put(MessageFields.NAME, ruleName + "_Variance");
		node.put(MessageFields.OUTPUT_FIELD_NAME, removeQuotes(outputFieldName));
		return node;
	}

	public static ObjectNode createPriceVariationNode(String processorId,
			String parentProcessorId, String outputField, String ruleName,
			String groupId) {
		ObjectNode node = ProcessorAttributeFiller
				.createBasicNodeWithMultipleParent(processorId,
						MessageFields.PRICE_VARIATION_TYPE, groupId,
						parentProcessorId);
		node.put(MessageFields.NAME, ruleName + "_PriceVariation");
		node.put(MessageFields.OUTPUT_FIELD_NAME, removeQuotes(outputField));
		return node;
	}

	public static ObjectNode createPublishNode(String processorId,
			String topicName, String groupId, String... parents) {
		ObjectNode node = ProcessorAttributeFiller
				.createBasicNodeWithMultipleParent(processorId,
						MessageFields.PUBLISH_TYPE, groupId, parents);
		node.put(MessageFields.TOPICNAME, removeQuotes(topicName));
		return node;
	}

	private static String removeQuotes(String name) {
		if (name.charAt(0) == '\"' && name.charAt(name.length() - 1) == '\"') {
			return name.substring(1, name.length() - 1);
		}
		return name;
	}

}
